﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace comm
{
    using NValueDic = Dictionary<string, objst.DNValue>;
    using LanguageDic = Dictionary<string, objst.DLanguage>;
    using StateDic = Dictionary<string, objst.DState>;
    using ObjDic = Dictionary<string, objst.DObject>;
    using HeroDic = Dictionary<string, objst.DHero>;
    using UnitDic = Dictionary<string, objst.DUnit>;
    using MonsterDic = Dictionary<string, objst.DMonster>;
    using UnitUpgradeDic = Dictionary<string, objst.DUnitUpgrade>;
    using MapDic = Dictionary<string, objst.DMap>;
    using StarDic = Dictionary<string, objst.DStar>;
    using ItemDic = Dictionary<string, objst.DItem>;
    using ItemCategoryDic = Dictionary<string, objst.DItemCategory>;
    using RankDic = Dictionary<string, objst.DRank>;
    using System.Collections;
    using System.Reflection;

    public interface IFindClassType
    {
        Type FindLibType(string classType);
        string[] GetAllLibType();
    }

    public class DefineFile
    {
        public static DefineFile LastInstance { get; private set; }
        static Dictionary<string, Type> _typeDic = new Dictionary<string, Type>();
        static Dictionary<string, string> _typeFullNameDic = new Dictionary<string, string>();
        #region static DefineFile() begin
        static DefineFile()
        {
        #endregion
            _typeDic.Add(TYPE_NVALUE, typeof(NValueDic));
            _typeDic.Add(TYPE_LANGUAGE, typeof(LanguageDic));
            _typeDic.Add(TYPE_RANK, typeof(RankDic));
            _typeDic.Add(TYPE_STATE, typeof(StateDic));
            _typeDic.Add(TYPE_OBJECT, typeof(ObjDic));
            _typeDic.Add(TYPE_HERO, typeof(HeroDic));
            _typeDic.Add(TYPE_UNIT, typeof(UnitDic));
            _typeDic.Add(TYPE_MONSTER, typeof(MonsterDic));
            _typeDic.Add(TYPE_UNIT_UPGRADE, typeof(UnitUpgradeDic));
            _typeDic.Add(TYPE_ITEM_CATEGORY, typeof(ItemCategoryDic));
            _typeDic.Add(TYPE_ITEM, typeof(ItemDic));
            _typeDic.Add(TYPE_MAP, typeof(MapDic));
            _typeDic.Add(TYPE_STAR, typeof(StarDic));

            foreach (var typeKey in _typeDic.Keys)
            {
                _typeFullNameDic.Add(_typeDic[typeKey].FullName, typeKey);
            }

            #region static DefineFile() end
        }
            #endregion

        public const string TYPE_NVALUE = "NValue";
        public const string TYPE_LANGUAGE = "Language";
        public const string TYPE_STATE = "State";
        public const string TYPE_OBJECT = "Object";
        public const string TYPE_HERO = "Hero";
        public const string TYPE_UNIT = "Unit";
        public const string TYPE_MONSTER = "Monster";
        public const string TYPE_UNIT_UPGRADE = "UnitUpgrade";
        public const string TYPE_MAP = "Map";
        public const string TYPE_STAR = "Star";
        public const string TYPE_ITEM = "Item";
        public const string TYPE_ITEM_CATEGORY = "ItemCategory";
        public const string TYPE_RANK = "Rank";

        public string Folder { get; private set; }
        public IFindClassType FindClassType { get; private set; }

        Dictionary<string, IDictionary> _valueDic = new Dictionary<string, IDictionary>();

        string _mainJSFile = "";

        public DefineFile(string folder, IFindClassType findClassType = null)
        {
            Folder = folder;
            FindClassType = findClassType;
            _mainJSFile = Path.Combine(Folder, "main.js");

            foreach (var type in _typeDic.Values)
            {
                _valueDic.Add(type.FullName, null);
            }

            Reload();

            LastInstance = this;
        }

        void ReadMainJS()
        {
            var code = ReadScriptFile(_mainJSFile);
            var sb = new StringBuilder();
            foreach (var typeKey in _typeDic.Keys)
            {
                sb.Append(typeKey + "|");
            }
            if (sb.Length > 0)
            {
                sb.Remove(sb.Length - 1, 1);
            }
            Regex.Replace(code, "(" + sb + @")\((.+?)\)", new MatchEvaluator(mh =>
            {
                var typeKey = mh.Groups[1].Value;
                if (_typeDic.ContainsKey(typeKey))
                {
                    _valueDic[_typeDic[typeKey].FullName] = (IDictionary)comm.JsonSerializer.Deserialize(_typeDic[typeKey], mh.Groups[2].Value);
                }
                return mh.Value;
            }), RegexOptions.Singleline);

            foreach (var typeKey in _typeDic.Keys)
            {
                if (_valueDic[_typeDic[typeKey].FullName] == null)
                {
                    throw new Exception(typeKey + "(...) not found");
                }
            }
        }
        string ReadScriptFile(string file)
        {
            var str = File.ReadAllText(file);
            str = Regex.Replace(str, @"include\(('|"")(.+)('|"")\)", new MatchEvaluator(mh =>
            {
                return File.ReadAllText(Path.Combine(Folder, mh.Groups[2].Value));
            }));
            return str;
        }
        T GetValue<T>()
        {
            return (T)_valueDic[typeof(T).FullName];
        }
        Type GetType(string name)
        {
            return _typeDic[name];
        }

        public void Reload()
        {
            ReadMainJS();

            #region verify
            FilePathTo(_valueDic, objst.URIType.ABSOLUTE);
            ReadValueDicProp(_valueDic, (key, obj, type, prop) =>
            {
                if (prop.Name.ToLower().IndexOf("file") != -1)
                {
                    var file = prop.GetValue(obj, null).ToString();
                    if (!File.Exists(file))
                    {
                        throw new Exception(key + " prop:" + prop.Name + " " + file + " not found!");
                    }
                }
                if (FindClassType != null && prop.Name.ToLower() == "classtype")
                {
                    var classType = prop.GetValue(obj, null).ToString();
                    var findType = FindClassType.FindLibType(classType);
                    if (findType == null)
                    {
                        throw new Exception(key + " prop:" + prop.Name + " " + classType + " not found!");
                    }
                }
            });
            #endregion

            //foreach (var typeKey in _typeDic.Keys)
            //{
            //    Console.WriteLine("--- " + typeKey + ":");

            //    Console.WriteLine(JsonSerializer.Serialize(_valueDic[_typeDic[typeKey].FullName]));
            //}
            //Console.WriteLine("\r\n\r\n");
        }
        public string ExportConst()
        {
            var sb = new StringBuilder();
            foreach (var key in _valueDic.Keys)
            {
                var dic = _valueDic[key];
                foreach (var key2 in dic.Keys)
                {
                    sb.Append("public const string " + comm.StringHelper.LineSplit(_typeFullNameDic[key]).ToUpper() + "_" + comm.StringHelper.LineSplit(key2.ToString()).ToUpper() + "=\"" + key2.ToString() + "\";\r\n");
                }
            }
            return sb.ToString();
        }
        public void WriteMainJS(Dictionary<string, IDictionary> valueDic)
        {
            var dic = new Dictionary<string, IDictionary>();
            foreach (var key in _typeDic.Keys)
            {
                dic.Add(key, new Dictionary<string, object>());
            }
            foreach (var key in valueDic.Keys)
            {
                if (dic.ContainsKey(key))
                {
                    dic[key] = valueDic[key];
                }
            }
            var sb = new StringBuilder();
            foreach (var key in dic.Keys)
            {
                sb.Append(key + "(" + comm.JsonSerializer.Serialize(dic[key], false) + ");\r\n\r\n");
            }
            File.WriteAllText(_mainJSFile, sb.ToString());
        }
        public void ReadValueDicProp(Dictionary<string, IDictionary> valueDic, Action<string, object, Type, PropertyInfo> callback)
        {
            foreach (var key in valueDic.Keys)
            {
                var dic = valueDic[key];
                foreach (var key2 in dic.Keys)
                {
                    var item = dic[key2];
                    var type = item.GetType();
                    foreach (var p in type.GetProperties())
                    {
                        callback(key + "," + key2, item, type, p);
                    }
                }
            }
        }
        public void FilePathTo(Dictionary<string, IDictionary> valueDic, comm.objst.URIType uriType)
        {
            ReadValueDicProp(valueDic, (key, obj, type, prop) =>
            {
                if (prop.Name.ToLower().IndexOf("file") != -1)
                {
                    var file = prop.GetValue(obj, null).ToString();
                    if (uriType == objst.URIType.ABSOLUTE)
                    {
                        prop.SetValue(obj, file.Replace("[ROOT]", Folder), null);
                    }
                    else if (uriType == objst.URIType.RELATIVE)
                    {
                        prop.SetValue(obj, file.Replace(Folder, "[ROOT]"), null);
                    }
                }
            });
        }

        public Dictionary<string, IDictionary> CloneValueDic()
        {
            var dic = new Dictionary<string, IDictionary>();
            foreach (var key in _valueDic.Keys)
            {
                dic.Add(_typeFullNameDic[key], _valueDic[key]);
            }
            return comm.BinarySerializer.Deserialize<Dictionary<string, IDictionary>>(comm.BinarySerializer.Serialize(dic));
        }

        #region object
        public string[] GetAllObject(objst.ObjType? objType = null)
        {
            var list = new List<string>();
            var dic = GetValue<ObjDic>();
            foreach (var key in dic.Keys)
            {
                if (objType.HasValue)
                {
                    if (objType.Value == dic[key].ObjType)
                    {
                        list.Add(key);
                    }
                }
                else
                {
                    list.Add(key);
                }
            }
            return list.ToArray();
        }
        public objst.DObject GetObject(string dObjectId)
        {
            objst.DObject obj = null;
            GetValue<ObjDic>().TryGetValue(dObjectId, out obj);
            return obj;
        }
        public objst.DObject.Data GetObjectData(string dObjectId)
        {
            var obj = GetObject(dObjectId);
            if (obj != null)
            {
                return BinarySerializer.Deserialize<objst.DObject.Data>(File.ReadAllBytes(obj.ObjFile));
            }
            //Console.WriteLine("dObject:" + dObjectId + " not found");
            return null;
        }
        #endregion

        #region hero
        public string[] GetAllHero()
        {
            var list = new List<string>();
            var dic = GetValue<HeroDic>();
            foreach (var key in dic.Keys)
            {
                list.Add(key);
            }
            return list.ToArray();
        }
        public objst.DHero GetHero(string heroId)
        {
            objst.DHero obj = null;
            GetValue<HeroDic>().TryGetValue(heroId, out obj);
            return obj;
        }
        #endregion

        #region unit
        public string[] GetAllUnit(objst.UnitType? unitType = null)
        {
            var g = "|";
            if (unitType.HasValue)
            {
                g += unitType.ToString() + "|";
            }
            else
            {
                var names = comm.EnumHelper.GetNames(typeof(objst.UnitType));
                foreach (var name in names)
                {
                    g += name + "|";
                }
            }

            var list = new List<string>();
            var dic = GetValue<UnitDic>();
            foreach (var key in dic.Keys)
            {
                if (g.IndexOf(dic[key].UnitType.ToString()) != -1)
                {
                    list.Add(key);
                }
            }
            return list.ToArray();
        }
        public objst.DUnit GetUnit(string unitId)
        {
            objst.DUnit obj = null;
            GetValue<UnitDic>().TryGetValue(unitId, out obj);
            return obj;
        }
        #endregion

        #region itemCate
        public ItemCategoryDic GetAllItemCate()
        {
            return GetValue<ItemCategoryDic>();
        }
        #endregion

        #region item
        public string[] GetAllItem(objst.ItemEquipType? equipType = null)
        {
            var g = "|";
            if (equipType.HasValue)
            {
                var types = objst.DItem.GetSubEquipTypeList(equipType.Value);
                g += equipType.ToString() + "|";
                foreach (var type in types)
                {
                    g += type + "|";
                }
            }
            else
            {
                var names = comm.EnumHelper.GetNames(typeof(objst.ItemEquipType));
                foreach (var name in names)
                {
                    g += name + "|";
                }
            }

            var list = new List<string>();
            var dic = GetValue<ItemDic>();
            foreach (var key in dic.Keys)
            {
                if (g.IndexOf(dic[key].EquipType.ToString()) != -1)
                {
                    list.Add(key);
                }
            }
            return list.ToArray();
        }
        public objst.DItem GetItem(string itemId)
        {
            objst.DItem obj = null;
            GetValue<ItemDic>().TryGetValue(itemId, out obj);
            return obj;
        }
        #endregion

        #region star
        public string[] GetAllStar()
        {
            var list = new List<string>();
            var dic = GetValue<StarDic>();
            foreach (var key in dic.Keys)
            {
                list.Add(key);
            }
            return list.ToArray();
        }
        public objst.DStar GetStar(string starId)
        {
            objst.DStar obj = null;
            GetValue<StarDic>().TryGetValue(starId, out obj);
            return obj;
        }
        public objst.DStar.Data GetStarData(string starId)
        {
            var star = GetStar(starId);
            if (star != null)
            {
                return BinarySerializer.Deserialize<objst.DStar.Data>(File.ReadAllBytes(star.File));
            }
            return null;
        }
        #endregion

        #region map
        public string[] GetAllMap()
        {
            var list = new List<string>();
            var mapDic = GetValue<MapDic>();
            foreach (var key in mapDic.Keys)
            {
                list.Add(key);
            }
            return list.ToArray();
        }
        public objst.DMap GetMap(string mapId)
        {
            objst.DMap map = null;
            GetValue<MapDic>().TryGetValue(mapId, out map);
            return map;
        }
        public objst.DMap.Data GetMapData(string mapId)
        {
            var map = GetMap(mapId);
            if (map != null)
            {
                return BinarySerializer.Deserialize<objst.DMap.Data>(File.ReadAllBytes(map.File));
            }
            return null;
        }
        #endregion

        #region nvalue
        public objst.DNValue GetNValue(string key)
        {
            objst.DNValue obj = null;
            GetValue<NValueDic>().TryGetValue(key, out obj);
            return obj;
        }
        public Dictionary<string, objst.DNValue> GetAllNValue(objst.NValueGroup? group = null, bool? save = null, objst.NValueBehaviorType? behaviorType = null)
        {
            var s1 = save.HasValue ? ("|" + save.Value + "|") : ("|" + true + "|" + false + "|");
            var s2 = "";
            if (group.HasValue)
            {
                s2 = "|" + group.Value + "|";
            }
            else
            {
                var names = EnumHelper.GetNames(typeof(objst.NValueGroup));
                foreach (var name in names)
                {
                    s2 += "|" + name + "|";
                }
            }

            var dic = new Dictionary<string, objst.DNValue>();
            var nDic = GetValue<NValueDic>();
            foreach (var key in nDic.Keys)
            {
                var item = nDic[key];
                if (s1.IndexOf("|" + item.Save + "|") != -1 &&
                    s2.IndexOf("|" + item.Group + "|") != -1 &&
                    (!behaviorType.HasValue || item.BehaviorTypeList.Contains(behaviorType.Value)))
                {
                    dic.Add(key, item);
                }
            }
            return dic;
        }
        public Dictionary<string, long> GetAllNVValue(objst.NValueGroup? group = null, bool? save = null)
        {
            var raw = GetAllNValue(group, save);
            var dic = new Dictionary<string, long>();
            foreach (var key in raw.Keys)
            {
                dic.Add(key, raw[key].Value);
            }
            return dic;
        }
        public string[] GetAllSaveNValue(objst.NValueGroup? group = null)
        {
            var list = new List<string>();
            var dic = GetAllNValue(group, true);
            foreach (var key in dic.Keys)
            {
                list.Add(key);
            }
            return list.ToArray();
        }
        #endregion

        #region state
        public string[] GetAllState(bool? save = null)
        {
            var s = save.HasValue ? ("|" + save.Value + "|") : ("|" + true + "|" + false + "|");
            var list = new List<string>();
            var stateDic = GetValue<StateDic>();
            foreach (var key in stateDic.Keys)
            {
                if (s.IndexOf("|" + stateDic[key].Save + "|") != -1)
                {
                    list.Add(key);
                }
            }
            return list.ToArray();
        }
        public string[] GetAllSaveState()
        {
            return GetAllState(true);
        }
        public objst.DState GetState(string stateId)
        {
            objst.DState state = null;
            GetValue<StateDic>().TryGetValue(stateId, out state);
            return state;
        }
        #endregion
    }
}
